using System;
using System.Collections.Generic;
using System.Text;

namespace JT808Protocol.Common
{
    /// <summary>
    /// 解析公共方法
    /// 只针对 BigEndian
    /// </summary>
    public class ParseHelper
    {
        /// <summary>
        /// 是否大端模式
        /// </summary>
        public static readonly bool IsBigEndianLocal;

        static ParseHelper()
        {
            IsBigEndianLocal = IsBigEncoding();
        }

        /// <summary>
        /// 还原转义
        /// </summary>
        /// <param name="byt">源数据</param>
        /// <param name="start"></param>
        /// <param name="length"></param>
        /// <returns></returns>
        public static byte[] EscRestore(ref byte[] byt, int start, int length)
        {
            List<byte> bytelis = new List<byte>();
            for (int i = start; i < start + length; i++)
            {
                byte s = byt[i];
                if (s == 0x7d)
                {
                    if ((i + 1) < byt.Length)
                    {
                        byte s1 = byt[i + 1];
                        if (s1 == 0x02)
                        {
                            bytelis.Add(0x7e);
                            i++;
                        }
                        else if (s1 == 0x01)
                        {
                            bytelis.Add(0x7d);
                            i++;
                        }
                        else
                            bytelis.Add(s);
                    }
                    else
                        bytelis.Add(s);
                }
                else
                    bytelis.Add(s);
            }
            return bytelis.ToArray();
        }

        /// <summary>
        /// 转义
        /// </summary>
        /// <param name="byt"></param>
        /// <returns></returns>
        public static List<byte> Esc(byte[] byt, int start, int end)
        {
            List<byte> bytelis = new List<byte>();
            for (int i = start; i < end + 1; i++)
            {
                byte s = byt[i];
                if (s == 0x7e)
                {
                    bytelis.Add(0x7d);
                    bytelis.Add(0x02);
                }
                else if (s == 0x7d)
                {
                    bytelis.Add(0x7d);
                    bytelis.Add(0x01);
                }
                else
                    bytelis.Add(s);
            }
            return bytelis;
        }

        /// <summary>
        /// 校验
        /// </summary>
        /// <param name="origianData">还原后(未转义)的原始数据，包括头尾标识位</param>
        /// <returns>计算出的校验码</returns>
        public static byte CheckSum(byte[] origianData, int start, int end)
        {
            byte bChecker = origianData[start];
            for (int i = start + 1, i_Count = end + 1; i < i_Count; i++)
            {
                bChecker = (byte)(bChecker ^ origianData[i]);
            }
            return bChecker;
        }

        /// <summary>
        /// 取 Byte 中的某一位,索引从 0 开始
        /// </summary>
        /// <param name="data"></param>
        /// <param name="index"></param>
        /// <returns></returns>
        public static bool Bit(byte data, int index)
        {
            if (index > 7)
            {
                throw new IndexOutOfRangeException("Byte位索引超出范围");
            }
            return ((data >> index) & 0x01) == 0x01;
        }

        /// <summary>
        /// 取 ushort 中的某一位,索引从 0 开始
        /// </summary>
        /// <param name="data"></param>
        /// <param name="index"></param>
        /// <returns></returns>
        public static int Bit(ushort data, int index)
        {
            if (index > 15)
            {
                throw new IndexOutOfRangeException("Byte位索引超出范围");
            }
            return (data >> index) & 0x01;
        }

        /// <summary>
        /// 取 uint 中的某一位,索引从 0 开始
        /// </summary>
        /// <param name="data"></param>
        /// <param name="index"></param>
        /// <returns></returns>
        public static bool Bit(uint data, int index)
        {
            if (index > 31)
            {
                throw new IndexOutOfRangeException("Byte位索引超出范围");
            }
            return ((int)(data >> index) & 0x01) == 0x01;
        }

        /// <summary>
        /// 本地大小端与大端模式转换
        /// 部标协议中网络采用大端模式传输数据
        /// 如果主机也是大端模式，则不需要转换
        /// </summary>
        /// <param name="Arr"></param>
        /// <param name="startIndex"></param>
        /// <param name="length"></param>
        /// <returns></returns>
        private static byte[] ConvertBigLocalEndian(byte[] arr)
        {
            if (!IsBigEndianLocal)
            {
                Array.Reverse(arr);
            }
            return arr;
        }

        /// <summary>
        /// 将 ushort 转成双字节
        /// </summary>
        /// <param name="data"></param>
        /// <returns></returns>
        public static byte[] Bytes(ushort data)
        {
            return ConvertBigLocalEndian(BitConverter.GetBytes(data));
        }

        /// <summary>
        /// 将 ushort 转成双字节
        /// </summary>
        /// <param name="data"></param>
        /// <returns></returns>
        public static byte[] Bytes(short data)
        {
            return ConvertBigLocalEndian(BitConverter.GetBytes(data));
        }

        /// <summary>
        /// 将 uint32 转成4字节
        /// </summary>
        /// <param name="data"></param>
        /// <returns></returns>
        public static byte[] Bytes(uint data)
        {
            return ConvertBigLocalEndian(BitConverter.GetBytes(data));
        }

        /// <summary>
        /// 将 uint32 转成4字节
        /// </summary>
        /// <param name="data"></param>
        /// <returns></returns>
        public static byte[] Bytes(int data)
        {
            return ConvertBigLocalEndian(BitConverter.GetBytes(data));
        }

        /// <summary>
        /// 将 ulong 转成8字节
        /// </summary>
        /// <param name="data"></param>
        /// <returns></returns>
        public static byte[] Bytes(ulong data)
        {
            return ConvertBigLocalEndian(BitConverter.GetBytes(data));
        }

        /// <summary>
        /// 获取 16 位值
        /// </summary>
        /// <param name="source"></param>
        /// <param name="startIndex"></param>
        /// <returns></returns>
        public static ushort Uint16(ref byte[] source, int startIndex)
        {
            var tmp = new byte[] { source[startIndex], source[startIndex + 1] };
            return BitConverter.ToUInt16(ConvertBigLocalEndian(tmp), 0);
        }

        /// <summary>
        /// 获取 16 位值
        /// </summary>
        /// <param name="source"></param>
        /// <param name="startIndex"></param>
        /// <returns></returns>
        public static ushort Uint16(ref ReadOnlySpan<byte> source, int startIndex)
        {
            var tmp = new byte[] { source[startIndex], source[startIndex + 1] };
            return BitConverter.ToUInt16(ConvertBigLocalEndian(tmp), 0);
        }

        /// <summary>
        /// 获取 32 位值
        /// </summary>
        /// <param name="source"></param>
        /// <param name="startIndex"></param>
        /// <returns></returns>
        public static uint Uint32(ref byte[] source, int startIndex)
        {
            var tmp = new byte[] { source[startIndex], source[startIndex + 1], source[startIndex + 2], source[startIndex + 3] };
            return BitConverter.ToUInt32(ConvertBigLocalEndian(tmp), 0);
        }

        /// <summary>
        /// 获取 32 位值
        /// </summary>
        /// <param name="source"></param>
        /// <param name="startIndex"></param>
        /// <returns></returns>
        public static uint Uint32(ref Span<byte> source, int startIndex)
        {
            var tmp = new byte[] { source[startIndex], source[startIndex + 1], source[startIndex + 2], source[startIndex + 3] };
            return BitConverter.ToUInt32(ConvertBigLocalEndian(tmp), 0);
        }

        /// <summary>
        /// 获取 32 位值
        /// </summary>
        /// <param name="source"></param>
        /// <param name="startIndex"></param>
        /// <returns></returns>
        public static uint Uint32(ref ReadOnlySpan<byte> source, int startIndex)
        {
            var tmp = new byte[] { source[startIndex], source[startIndex + 1], source[startIndex + 2], source[startIndex + 3] };
            return BitConverter.ToUInt32(ConvertBigLocalEndian(tmp), 0);
        }

        /// <summary>
        /// 是否大端模式
        /// </summary>
        /// <returns></returns>
        private static bool IsBigEncoding()
        {
            ushort seed = 1;
            byte[] byteUI1 = BitConverter.GetBytes(seed);
            return byteUI1[0] == 0;
        }

        /// <summary>
        /// BCD 码转字符串
        /// </summary>
        /// <param name="bcdData"></param>
        /// <returns></returns>
        public static string BcdToString(byte bcdData)
        {
            return (bcdData >> 4).ToString() + (bcdData & 0x0f).ToString();
        }

        /// <summary>
        /// 将 BCD 字节转成部标时间（6字节）(yyMMdd HHmmss)
        /// </summary>
        /// <param name="data"></param>
        /// <returns></returns>
        public static DateTime BcdTo1078Datetime(ref byte[] data, int startIndex)
        {
            return new DateTime((data[startIndex] >> 4) * 10 + (data[startIndex] & 0x0F) + 2000
                , (data[startIndex + 1] >> 4) * 10 + (data[startIndex + 1] & 0x0F)
                , (data[startIndex + 2] >> 4) * 10 + (data[startIndex + 2] & 0x0F)
                , (data[startIndex + 3] >> 4) * 10 + (data[startIndex + 3] & 0x0F)
                , (data[startIndex + 4] >> 4) * 10 + (data[startIndex + 4] & 0x0F)
                , (data[startIndex + 5] >> 4) * 10 + (data[startIndex + 5] & 0x0F)
                );
        }

        /// <summary>
        /// 将 BCD 字节转成部标时间（6字节）(yyMMdd HHmmss)
        /// </summary>
        /// <param name="data"></param>
        /// <returns></returns>
        public static DateTime BcdTo1078Datetime(ref ReadOnlySpan<byte> data, int startIndex)
        {
            return new DateTime((data[startIndex] >> 4) * 10 + (data[startIndex] & 0x0F) + 2000
                , (data[startIndex + 1] >> 4) * 10 + (data[startIndex + 1] & 0x0F)
                , (data[startIndex + 2] >> 4) * 10 + (data[startIndex + 2] & 0x0F)
                , (data[startIndex + 3] >> 4) * 10 + (data[startIndex + 3] & 0x0F)
                , (data[startIndex + 4] >> 4) * 10 + (data[startIndex + 4] & 0x0F)
                , (data[startIndex + 5] >> 4) * 10 + (data[startIndex + 5] & 0x0F)
                );
        }

        /// <summary>
        /// 将 BCD 字节转成部标时间（4字节）(yyyyMMdd)
        /// </summary>
        /// <param name="data"></param>
        /// <returns></returns>
        public static DateTime BcdTo1078Date(ref byte[] data, int startIndex)
        {
            return new DateTime((data[startIndex] >> 4) * 1000 + (data[startIndex] & 0x0F) * 100
                + (data[startIndex + 1] >> 4) * 10 + (data[startIndex + 1] & 0x0F)
                , (data[startIndex + 2] >> 4) * 10 + (data[startIndex + 2] & 0x0F)
                , (data[startIndex + 3] >> 4) * 10 + (data[startIndex + 3] & 0x0F)
                );
        }

        /// <summary>
        /// 将 BCD 字节转成部标时间（4字节）(yyyyMMdd)
        /// </summary>
        /// <param name="data"></param>
        /// <returns></returns>
        public static DateTime BcdTo1078Date(ref ReadOnlySpan<byte> data, int startIndex)
        {
            return new DateTime((data[startIndex] >> 4) * 1000 + (data[startIndex] & 0x0F) * 100
                + (data[startIndex + 1] >> 4) * 10 + (data[startIndex + 1] & 0x0F)
                , (data[startIndex + 2] >> 4) * 10 + (data[startIndex + 2] & 0x0F)
                , (data[startIndex + 3] >> 4) * 10 + (data[startIndex + 3] & 0x0F)
                );
        }

        /// <summary>
        /// 将时间转换成部标
        /// </summary>
        /// <param name="time"></param>
        /// <returns></returns>
        public static byte[] DatetimeToBCD(DateTime time)
        {
            var ret = new byte[6];
            var temp = time.Year % 100;
            ret[0] = (byte)((Convert.ToByte(temp / 10) << 4) | Convert.ToByte(temp % 10));
            temp = time.Month;
            ret[1] = (byte)((Convert.ToByte(temp / 10) << 4) | Convert.ToByte(temp % 10));
            temp = time.Day;
            ret[2] = (byte)((Convert.ToByte(temp / 10) << 4) | Convert.ToByte(temp % 10));
            temp = time.Hour;
            ret[3] = (byte)((Convert.ToByte(temp / 10) << 4) | Convert.ToByte(temp % 10));
            temp = time.Minute;
            ret[4] = (byte)((Convert.ToByte(temp / 10) << 4) | Convert.ToByte(temp % 10));
            temp = time.Second;
            ret[5] = (byte)((Convert.ToByte(temp / 10) << 4) | Convert.ToByte(temp % 10));
            return ret;
        }

        /// <summary>
        /// string 转 BCD 字节码
        /// </summary>
        /// <param name="data"></param>
        /// <returns></returns>
        public static byte[] StringToBCD(string source, ushort length = 0)
        {
            // 先整理长度
            var data = source;
            if (length != 0 && source.Length != length)
            {
                if (data.Length > length)
                {
                    data = source.Substring(data.Length - length);
                }
                else if (data.Length < length)
                {
                    var tempBuilder = new StringBuilder(length);
                    for (int i = data.Length; i < length; i++)
                    {
                        tempBuilder.Append('0');
                    }
                    tempBuilder.Append(data);
                }
            }
            // 判断奇偶
            var mod = data.Length & 1;
            var bcdArr = new byte[(data.Length / 2) + mod];
            if (mod != 0)
            {
                bcdArr[0] = Convert.ToByte(data[0].ToString());
            }
            // 转换 byte
            for (int i = mod + 1; i < data.Length; i += 2)
            {
                bcdArr[i / 2] = (byte)(((data[i - 1] - '0') << 4) | (data[i] - '0'));
            }
            return bcdArr;
        }

        /// <summary>
        /// 从 ASCII 编码的字符串获得字节数组
        /// </summary>
        /// <param name="str"></param>
        /// <returns></returns>
        public static byte[] AsciiBytes(string str)
        {
            return Encoding.ASCII.GetBytes(str);
        }

        /// <summary>
        /// 从GBK编码的字符串获得字节数组
        /// </summary>
        /// <param name="str"></param>
        /// <returns></returns>
        public static byte[] GbkBytes(string str)
        {
            var en = Encoding.GetEncoding("GBK");
            return en.GetBytes(str);
        }

        /// <summary>
        ///
        /// </summary>
        /// <param name="bytes"></param>
        /// <param name="startIndex"></param>
        /// <param name="count"></param>
        /// <returns></returns>
        public static string Gb2312String(ref byte[] bytes, int startIndex, int count)
        {
            var en = Encoding.GetEncoding("GB2312");
            return en.GetString(bytes, startIndex, count).Trim().Trim('\0');
        }

        public static string Gb2312String(ref ReadOnlySpan<byte> bytes, int startIndex, int count)
        {
            var en = Encoding.GetEncoding("GB2312");
            return en.GetString(bytes.ToArray(), startIndex, count).Trim().Trim('\0');
        }

        /// <summary>
        /// 获取 ascii 码的字符串形式
        /// </summary>
        /// <param name="bytes"></param>
        /// <param name="startIndex"></param>
        /// <param name="count"></param>
        /// <returns></returns>
        public static string AsciiString(ref ReadOnlySpan<byte> bytes, int startIndex, int count)
        {
            return Encoding.ASCII.GetString(bytes.ToArray(), startIndex, count).Trim();
        }

        /// <summary>
        /// 从汉字转换到16进制
        /// </summary>
        /// <param name="s"></param>
        /// <returns></returns>
        public static string Gb2312HexString(string s)
        {
            if ((s.Length % 2) != 0)
            {
                s += " ";//空格
            }

            Encoding chs = Encoding.GetEncoding("gb2312");

            byte[] bytes = chs.GetBytes(s);

            string str = "";

            for (int i = 0; i < bytes.Length; i++)
            {
                str += string.Format("{0:X}", bytes[i]);
            }

            return str;
        }

        /// <summary>
        /// 16进制字符串转字节数组
        /// </summary>
        /// <param name="hexString"></param>
        /// <returns></returns>
        public static byte[] HexStrToBytes(string hexString)
        {
            //按编码习惯替换
            hexString = hexString.Replace(",", "").Replace(" ", "").Replace("-", "");

            byte[] hexBytes = new byte[hexString.Length / 2];
            for (int i = 0; i < hexString.Length / 2;)
            {
                string hex = hexString.Substring(i * 2, 2);
                hexBytes[i] = Convert.ToByte(hex, 16);
                i++;
            }

            return hexBytes;
        }

        /// <summary>
        /// 16进制(0x00~0xff)字符串转byte
        /// </summary>
        /// <param name="s"></param>
        /// <returns></returns>
        public static byte Byte(string hexStr)
        {
            var s1 = hexStr.Substring(2); // 截取后两位
            return Convert.ToByte(s1, 16);
        }
    }
}